""" 被统计对象模块

用于描述被统计对象的类：
文件（File）、仓库（Repo）
用于组织被统计对象的类：
组织（Organization）、版本（Version）、提交（Commit）
用于记录被统计对象信息的类：
贡献（Contribution）、版权（Copyright）

"""
import logging
import os
import re
from abc import ABCMeta, abstractmethod

from code_metric.config import init_global_config_setting
from code_metric.utils import write_as_excel, get_json_response_list_from_url_list, read_excel_to_dict_list, \
    run_command_with_feedback, singleton, get_date, get_email_host_to_com_dict
from code_metric.utils_str import form_abs_name_for_org_repos_info_file, parse_repo_info_into_dict, \
    parse_owner_and_repo_name_to_repo_url, \
    form_dir_to_store_org_repo, form_abs_name_for_repo_cloc_excel_file, parse_file_path_to_underscore_name, \
    parse_repo_url_to_owner_and_repo_name, parse_email_to_host, parse_file_path_to_file_type, \
    parse_ref_location_from_ref_type
from code_metric.metric import Cloc, GitBlame, GitShow

CONFIG = init_global_config_setting()
BASE_DIR = os.path.dirname(os.path.abspath(__file__))


class VersionInfoHandler(metaclass=ABCMeta):

    @abstractmethod
    def get_version_released_time(self) -> str:
        pass

    @abstractmethod
    def get_version_repos_ref(self) -> ():
        pass

    @abstractmethod
    def get_version_repos_info_file(self) -> str:
        pass

    @abstractmethod
    def init_version_repos_info_file(self) -> str:
        pass


class DefaultVIHandler(VersionInfoHandler):
    def __init__(self, version_name='master', owner_name='openharmony'):
        self.owner_name = owner_name
        self.ref = version_name
        self.version_repos_info_file = Organization(self.owner_name).org_repos_info_file

    def get_version_repos_info_file(self):
        return self.version_repos_info_file

    def get_version_released_time(self):
        return get_date()

    def get_version_repos_ref(self):
        ref_name = self.ref
        ref_type = 'Branch'
        return ref_name, ref_type

    def init_version_repos_info_file(self, renew_now=False):
        if renew_now or not os.path.exists(self.version_repos_info_file):
            Organization(self.owner_name).init_repos_info_excel()
        return self.version_repos_info_file


class Version:
    def __init__(self, owner_name, version_name='master', info_handler: VersionInfoHandler = None):
        self.owner_name = owner_name
        self.name = version_name
        self.info_handler = DefaultVIHandler(version_name, owner_name) if info_handler is None else info_handler
        self.version_repos_info_file = self.info_handler.get_version_repos_info_file()
        self._released_time = ''
        self._repos_ref = ()
        self._repo_url_list = []
        self._repo_object_list = []

    def get_released_time(self):
        if not self._released_time:
            self._released_time = self.info_handler.get_version_released_time()
        return self._released_time

    def get_unified_version_ref(self):
        if not self._repos_ref:
            self._repos_ref = self.info_handler.get_version_repos_ref()
        return self._repos_ref

    def get_repo_object_list(self, renew_now=False):
        if not self._repo_object_list:
            self._set_repo_object_list(renew_now)
        return self._repo_object_list

    def _set_repo_object_list(self, renew_now):
        ref, ref_type = self.get_unified_version_ref()
        for repo_url in self.get_repo_url_list(renew_now):
            owner_name, repo_name = parse_repo_url_to_owner_and_repo_name(repo_url)
            self._repo_object_list.append(Repo(owner_name, repo_name, ref=ref, ref_type=ref_type))

    def get_repo_url_list(self, renew_now=False):  # TODO: 考虑扩展信息的提取，具名元组
        if not self._repo_url_list:
            self._set_repo_url_list(renew_now)
        return self._repo_url_list

    def _set_repo_url_list(self, renew_now):
        version_info_dict = read_excel_to_dict_list(self.init_version_repos_info_file(renew_now))
        for idx, version_info in enumerate(version_info_dict):
            self._repo_url_list.append(version_info['仓库链接'])

    def init_version_repos_info_file(self, renew_now=False):
        if renew_now or not os.path.isfile(self.version_repos_info_file):
            self.version_repos_info_file = self.info_handler.init_version_repos_info_file()
        return self.version_repos_info_file

    def init_version_repos_data(self, renew_now=False):
        print(f'正在初始化{self.owner_name}版本{self.name}的各仓库...')
        repo_list = self.get_repo_object_list(renew_now)
        for idx, repo in enumerate(repo_list):
            print(f'目前{idx + 1}/{len(repo_list)}仓{repo.name}')
            repo.init_repo_data()


class Commit:
    def __init__(self, repo, commit_id):
        self.repo = repo
        self.id = commit_id
        self._contribution_obj_list = []

    def get_contribution_obj_list(self, renew_now=False):
        if not self._contribution_obj_list:
            self._set_contribution_obj_list(renew_now)
        return self._contribution_obj_list

    def _set_contribution_obj_list(self, renew_now):
        show = GitShow(self.repo, self)
        contribution_info_list = read_excel_to_dict_list(show.get_commit_show_excel(renew_now))
        for contribution in contribution_info_list:
            file = File(self.repo, contribution['文件名称'])
            self._contribution_obj_list.append(
                Contribution(file, contribution['贡献者邮箱'], insert_lines=contribution['增加行数'],
                             delete_lines=contribution['减少行数']))


class Organization:
    def __init__(self, org_name):
        self.name = org_name
        self.org_repos_info_file = form_abs_name_for_org_repos_info_file(org_name)
        self._repo_object_list = []

    def get_repo_object_list(self):
        if not self._repo_object_list:
            self._set_repo_object_list()
        return self._repo_object_list

    def init_repos_info_excel(self, renew_now=False):
        if renew_now or not os.path.exists(self.org_repos_info_file):
            self._generate_repos_info_excel_for_org()
        return self.org_repos_info_file

    def _set_repo_object_list(self):
        self.init_repos_info_excel()
        repos_info_dict = read_excel_to_dict_list(self.org_repos_info_file)
        for idx, repo_info in enumerate(repos_info_dict):
            repo_name = repo_info['仓库名称']
            repo = Repo(self.name, repo_name)
            self._repo_object_list.append(repo)

    def _generate_repos_info_excel_for_org(self):
        repos_info_list = []
        for repo_info in self._get_repos_info_list_for_org():
            repos_info_list.append(parse_repo_info_into_dict(repo_info))
        columns_order = ['仓库名称', '仓库链接', '仓库描述', '仓库状态', '创建时间']
        write_as_excel(self.org_repos_info_file, repos_info_list, columns_order)

    def _get_repos_info_list_for_org(self):
        url_list = self._form_api_list_need_to_request()
        repos_info_list = get_json_response_list_from_url_list(url_list)
        return repos_info_list

    def _form_api_list_need_to_request(self):
        api_list = []
        request_url = "https://gitee.com/api/v5/orgs/{}/repos?access_token={}&type=all&per_page=100&page={}"
        for page_num in range(1, CONFIG['max_page_for_repos']):
            api_url = request_url.format(self.name, CONFIG['access_token'], page_num)
            api_list.append(api_url)
        return api_list


class Repo:
    def __init__(self, owner_name, repo_name, ref='master', ref_type='Branch'):
        self.owner_name = owner_name
        self.name = repo_name
        self.url = parse_owner_and_repo_name_to_repo_url(owner_name, repo_name)
        self.ref = ref
        self.ref_type = ref_type
        self.clone_to_where = form_dir_to_store_org_repo(self.owner_name)
        self.path = os.path.join(self.clone_to_where, repo_name)
        self._file_object_list = []

    def init_repo_data(self, ref=None, ref_type=None, shallow=False):
        print(f'正在初始化仓库{self.name}...')
        if ref is not None:
            self.ref = ref
        if ref_type is not None:
            self.ref_type = ref_type
        if not os.path.exists(self.path):
            self._clone_repo(shallow=shallow)
        else:
            self._switch_and_update_repo(shallow=shallow)

    def _clone_repo(self, shallow=False):
        cmd = ['git', 'clone', self.url, '-b', self.ref]
        if shallow:
            cmd.append('--depth=1')
        clone_to_where = form_dir_to_store_org_repo(self.owner_name)
        run_command_with_feedback(cmd, cwd=clone_to_where, encoding='utf-8')

    def _switch_and_update_repo(self, shallow=False):
        cwd = self.path
        cmd = ['git', 'remote', 'set-branches', 'origin', self.ref]
        run_command_with_feedback(cmd, cwd=cwd, encoding='utf-8')

        cmd = ['git', 'fetch', 'origin', self.ref]
        if shallow:
            cmd.append('--depth=1')
        run_command_with_feedback(cmd, cwd=cwd, encoding='utf-8')

        cmd = ['git', 'checkout', '-f', self.ref]
        run_command_with_feedback(cmd, cwd=cwd, encoding='utf-8')

        cmd = ['git', 'reset', '--hard']
        ref_location = parse_ref_location_from_ref_type(self.ref_type)
        cmd.append(ref_location + self.ref)
        run_command_with_feedback(cmd, cwd=cwd, encoding='utf-8')

    def get_file_object_list(self, renew_now=False):
        if not self._file_object_list:
            self._set_file_object_list(renew_now)
        return self._file_object_list

    def _set_file_object_list(self, renew_now):
        file_name_to_path_list = self._get_repo_file_name_list(renew_now)
        for file_name in file_name_to_path_list:
            repo_file = File(self, file_name)
            self._file_object_list.append(repo_file)

    def _get_repo_file_name_list(self, renew_now):
        file_name_to_path_list = []
        repo_all_file_list = self._get_file_list_in_repo_from_cloc(renew_now)
        for idx, file_name in enumerate(repo_all_file_list):
            file_name_to_path_list.append(file_name)
        return file_name_to_path_list

    def _get_file_list_in_repo_from_cloc(self, renew_now):
        all_file_list = []
        with open(Cloc(self).get_repo_cloc_txt(renew_now), 'r', encoding='utf-8') as log_file:
            for f_line in log_file.readlines():
                if f_line.strip().startswith(self.name):
                    array = re.split(r'\s{5,}', f_line)
                    all_file_list.append(array[0])
        return all_file_list


class File:
    def __init__(self, repo, file_path_name):
        self.repo = repo
        self.name = file_path_name
        self.path = '\\\?\\' + os.path.join(repo.clone_to_where, file_path_name)
        self.type = parse_file_path_to_file_type(file_path_name)
        self.flat_name = parse_file_path_to_underscore_name(file_path_name)
        self._copyright = None
        self._contribution_obj_list = []

    def get_copyright(self):
        if not self._copyright:
            self._set_file_copyright()
        return self._copyright

    def _set_file_copyright(self):
        self._copyright = Copyright(self.repo, self).get_copyright()

    def get_contribution_obj_list(self, renew_now=False):
        if not self._contribution_obj_list:
            self._set_contribution_obj_list(renew_now)
        return self._contribution_obj_list

    def _set_contribution_obj_list(self, renew_now):
        blame = GitBlame(self.repo, self)
        author_info_list = read_excel_to_dict_list(blame.get_repo_blame_excel(renew_now))
        for author_info in author_info_list:
            email = author_info['贡献者邮箱']
            lines = author_info['贡献行数']
            self._contribution_obj_list.append(Contribution(self, email, lines))


class Contribution:
    def __init__(self, file, email, stocks_lines=-1, insert_lines=-1, delete_lines=-1):
        self.file = file
        self.email = email
        self._email_host = ''
        self._company = ''
        self.stocks_lines = stocks_lines
        self.insert_lines = insert_lines
        self.delete_lines = delete_lines

    def get_email_host(self):
        if not self._email_host:
            self._set_email_host()
        return self._email_host

    def _set_email_host(self):
        self._email_host = parse_email_to_host(self.email)

    def get_author_company(self):
        if not self._company:
            self._set_author_company()
        return self._company

    def _set_author_company(self):
        host_com_dict = get_email_host_to_com_dict()
        self._company = host_com_dict.get(self.get_email_host(), '未知贡献者')


class Copyright:
    def __init__(self, repo, file):
        self.repo = repo
        self.file = file
        self._copyright = ''

    def get_copyright(self):
        if not self._copyright:
            self._set_copyright()
        return self._copyright

    def _set_copyright(self):
        try:
            with open(self.file.path, 'r', encoding='UTF-8', errors='ignore') as f:
                for line in f.readlines():
                    line = line.lower()
                    if 'copyright' in line and '(c)' in line:
                        self._copyright = re.search(r'c.*$', line).group()
                        break
        except Exception as message:
            logging.error('打开文件，尝试判断版权时，发生异常：' + repr(message))
        if not self._copyright:
            self._copyright = CONFIG['no_copyright']


if __name__ == '__main__':
    pass
